
// UstavSRDlg.cpp : implementation file
//
#include "stdafx.h"
#include "UstavSR.h"
#include "UstavSRDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#pragma warning(disable : 4996)

BEGIN_MESSAGE_MAP(CUstavSRDlg, CDialogEx)
    ON_WM_SERIAL(&CUstavSRDlg::OnSerialMsg)
    ON_BN_CLICKED(IDC_BUTTON_OPEN_CLOSE_PORT, &CUstavSRDlg::OnBnClickedButtonOpenClosePort)
    ON_BN_CLICKED(IDC_BUTTON_SEND_DATA, &CUstavSRDlg::OnBnClickedButtonSendData)
    ON_BN_CLICKED(IDC_BUTTON_SEND_STATUS, &CUstavSRDlg::OnBnClickedButtonSendStatus)
    ON_BN_CLICKED(IDC_RADIO_MODE_SERVER, &CUstavSRDlg::OnBnClickedRadioModeServer)
    ON_BN_CLICKED(IDC_RADIO_MODE_ROUTER, &CUstavSRDlg::OnBnClickedRadioModeRouter)
END_MESSAGE_MAP()

// CUstavSRDlg dialog
CUstavSRDlg::CUstavSRDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CUstavSRDlg::IDD, pParent)
    , m_yaw(0)
    , m_pitch(0)
    , m_roll(0)
    , m_heading(0)
    , m_latitude(0)
    , m_longitude(0)
    , m_altitude(0)
    , m_airSpeed(0)
    , m_groundSpeed(0)
    , m_consumption(0)
    , m_batteryLife(0)
    , m_health(0)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CUstavSRDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_COMBO_PORT, m_cb_port);
    DDX_Control(pDX, IDC_COMBO_BAUD, m_cb_baud);
    DDX_Control(pDX, IDC_COMBO_PARITY, m_cb_parity);
    DDX_Control(pDX, IDC_COMBO_DATA_BITS, m_cb_dataBits);
    DDX_Control(pDX, IDC_COMBO_STOP_BITS, m_cb_stopBits);
    DDX_Control(pDX, IDC_COMBO_FLOW_CONTROL, m_cb_flowControl);
    DDX_Control(pDX, IDC_EDIT_LOG, m_logBox);
    DDX_Control(pDX, IDC_BUTTON_OPEN_CLOSE_PORT, m_b_openClosePort);
    DDX_Control(pDX, IDC_BUTTON_SEND_DATA, m_b_sendData);
    DDX_Control(pDX, IDC_BUTTON_SEND_STATUS, m_b_sendStatus);
    // USData
    DDX_Text(pDX, IDC_EDIT_YAW, m_yaw);
    DDV_MinMaxInt(pDX, m_yaw, US_DATA_YAW_MIN, US_DATA_YAW_MAX);
    DDX_Text(pDX, IDC_EDIT_PITCH, m_pitch);
    DDV_MinMaxInt(pDX, m_pitch, US_DATA_PITCH_MIN, US_DATA_PITCH_MAX);
    DDX_Text(pDX, IDC_EDIT_ROLL, m_roll);
    DDV_MinMaxInt(pDX, m_roll, US_DATA_ROLL_MIN, US_DATA_ROLL_MAX);
    DDX_Text(pDX, IDC_EDIT_HEADING, m_heading);
    DDV_MinMaxInt(pDX, m_heading, US_DATA_HEADING_MIN, US_DATA_HEADING_MAX);
    DDX_Text(pDX, IDC_EDIT_POS_LATITUDE, m_latitude);
    DDV_MinMaxDouble(pDX, m_latitude, US_DATA_LATITUDE_MIN, US_DATA_LATITUDE_MAX);
    DDX_Text(pDX, IDC_EDIT_LONGITUDE, m_longitude);
    DDV_MinMaxDouble(pDX, m_longitude, US_DATA_LONGITUDE_MIN, US_DATA_LONGITUDE_MAX);
    DDX_Text(pDX, IDC_EDIT_ALTITUDE, m_altitude);
    DDV_MinMaxFloat(pDX, m_altitude, US_DATA_ALTITUDE_MIN, US_DATA_ALTITUDE_MAX);
    DDX_Text(pDX, IDC_EDIT_AIRSPEED, m_airSpeed);
    DDV_MinMaxFloat(pDX, m_airSpeed, US_DATA_AIRSPEED_MIN, US_DATA_AIRSPEED_MAX);
    DDX_Text(pDX, IDC_EDIT_GROUND_SPEED, m_groundSpeed);
    DDV_MinMaxFloat(pDX, m_groundSpeed, US_DATA_GROUNDSPEED_MIN, US_DATA_GROUNDSPEED_MAX);
    // USStatus
    DDX_Text(pDX, IDC_EDIT_CONSUMPTION, m_consumption);
    DDV_MinMaxInt(pDX, m_consumption, US_STATUS_CONSUMPTION_MIN, US_STATUS_CONSUMPTION_MAX);
    DDX_Text(pDX, IDC_EDIT_BATTERY_LIFE, m_batteryLife);
    DDV_MinMaxInt(pDX, m_batteryLife, US_STATUS_BATTERYLIFE_MIN, US_STATUS_BATTERYLIFE_MAX);
    DDX_Text(pDX, IDC_EDIT_UAV_HEALTH, m_health);
    DDV_MinMaxInt(pDX, m_health, US_STATUS_HEALTH_MIN, US_STATUS_HEALTH_MAX);
    DDX_Control(pDX, IDC_BUTTON_SEND_GOTO, m_b_sendGoto);
    DDX_Control(pDX, IDC_BUTTON_SEND_LIGHTS, m_b_sendLights);
}

// CUstavSRDlg message handlers
BOOL CUstavSRDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);			// Set big icon
    SetIcon(m_hIcon, FALSE);		// Set small icon

    // Server is default mode
    ((CButton*)GetDlgItem(IDC_RADIO_MODE_SERVER))->SetCheck(BST_CHECKED);
    OnBnClickedRadioModeServer();

    // Fill in combo boxes
    InitSerialControls();
    
    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CUstavSRDlg::InitSerialControls()
{
    // Get Available Ports and fill in ports combobox
    CString portName;
    for (int portNumber = 1; portNumber < 256; ++portNumber)
    {
        if (portNumber < 10) 
        {
            portName.Format(_T("COM%d"), portNumber);
        }
        else
        {
            portName.Format(_T("\\\\.\\COM%d"), portNumber);
        }

        HANDLE hFile = ::CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
             OPEN_EXISTING, 0, NULL);
        
        if (hFile != INVALID_HANDLE_VALUE)
            m_cb_port.AddString(portName);
        else
        {
            DWORD err = GetLastError();
            if (err == ERROR_ACCESS_DENIED)
            {
                LogMessage(_T("Access denied for: ") + portName);
            }
            else if (err == ERROR_SHARING_VIOLATION)
            {
                LogMessage(_T("Error Sharing violation for: ") + portName);
            }
        }
        CloseHandle(hFile);
    }
    m_cb_port.SetCurSel(0);

    // Fill in baud combobox
    char pszNum [32] = {0};
    m_cb_baud.AddString(_itoa (CSerial::EBaud110, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud300, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud600, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud1200, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud2400, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud4800, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud9600, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud14400, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud19200, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud38400, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud56000, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud57600, pszNum, 10));    // 11
	m_cb_baud.AddString(_itoa (CSerial::EBaud115200, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud128000, pszNum, 10));
	m_cb_baud.AddString(_itoa (CSerial::EBaud256000, pszNum, 10));
    m_cb_baud.SetCurSel(11);

    //  Fill in parity map and parity combobox
    m_parityMap["No Parity"] = CSerial::EParNone;
    m_parityMap["Odd Parity"] = CSerial::EParOdd;
    m_parityMap["Even Parity"] = CSerial::EParEven;
    m_parityMap["Mark Parity"] = CSerial::EParMark;
    m_parityMap["Space Parity"] = CSerial::EParSpace;
    m_cb_parity.AddString("No Parity");
    m_cb_parity.AddString("Odd Parity");
    m_cb_parity.AddString("Even Parity");
    m_cb_parity.AddString("Mark Parity");
    m_cb_parity.AddString("Space Parity");
    m_cb_parity.SetCurSel(0);

    // Fill in data bits combobox
    m_cb_dataBits.AddString(_itoa (CSerial::EData5, pszNum, 10));
    m_cb_dataBits.AddString(_itoa (CSerial::EData6, pszNum, 10));
    m_cb_dataBits.AddString(_itoa (CSerial::EData7, pszNum, 10));
    m_cb_dataBits.AddString(_itoa (CSerial::EData8, pszNum, 10)); // 3
    m_cb_dataBits.SetCurSel(3);

    // Fill in stop bits stop bits map and combobox
    m_stopBitsMap["1 stopbit"] = CSerial::EStop1;
    m_stopBitsMap["1.5 stopbit"] = CSerial::EStop1_5;
    m_stopBitsMap["2 stopbits"] = CSerial::EStop2;
    m_cb_stopBits.AddString("1 stopbit");
    m_cb_stopBits.AddString("1.5 stopbit");
    m_cb_stopBits.AddString("2 stopbits");
    m_cb_stopBits.SetCurSel(0);

    // Fill in flow control map and flow control combobox
    m_flowControlMap["No handshaking"] = CSerial::EHandshakeOff;
    m_flowControlMap["Hardware handshaking (RTS/CTS)"] = CSerial::EHandshakeHardware;
    m_flowControlMap["Software handshaking (XON/XOFF)"] = CSerial::EHandshakeSoftware;
    m_cb_flowControl.AddString("No handshaking");
    m_cb_flowControl.AddString("Hardware handshaking (RTS/CTS)");
    m_cb_flowControl.AddString("Software handshaking (XON/XOFF)");
    m_cb_flowControl.SetCurSel(0);
}

void CUstavSRDlg::LogMessage(LPCTSTR pszText)
{
   // get the initial text length
   int nLength = m_logBox.GetWindowTextLength();
   // put the selection at the end of text
   m_logBox.SetSel(nLength, nLength);
   // replace the selection
   m_logBox.ReplaceSel(CString(pszText) + char(13) + char(10));
}

void CUstavSRDlg::EnableModeControlsGroup(BOOL enable)
{
    GetDlgItem(IDC_STATIC_GROUP_MODE)->EnableWindow(enable);
    GetDlgItem(IDC_RADIO_MODE_SERVER)->EnableWindow(enable);
    GetDlgItem(IDC_RADIO_MODE_ROUTER)->EnableWindow(enable);
}

void CUstavSRDlg::EnableSendButtons(BOOL enable)
{
    // Enable/Disable for server mode
    if (((CButton*)GetDlgItem(IDC_RADIO_MODE_SERVER))->GetCheck() == BST_CHECKED)
    {
        m_b_sendData.EnableWindow(enable);
        m_b_sendStatus.EnableWindow(enable);
    }
    
    // Enable/Disable for router mode
    if (((CButton*)GetDlgItem(IDC_RADIO_MODE_ROUTER))->GetCheck() == BST_CHECKED)
    {
        m_b_sendGoto.EnableWindow(enable);
        m_b_sendLights.EnableWindow(enable);
    }
}

void CUstavSRDlg::EnableSerialControls(BOOL enable)
{
    m_cb_port.EnableWindow(enable);
    m_cb_baud.EnableWindow(enable);
    m_cb_parity.EnableWindow(enable);
    m_cb_dataBits.EnableWindow(enable);
    m_cb_stopBits.EnableWindow(enable);
    m_cb_flowControl.EnableWindow(enable);
}

void CUstavSRDlg::EnableNetworkControlsGroup(BOOL enable)
{
    GetDlgItem(IDC_STATIC_GROUP_NETWORK)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_NETWORK_CLIENT_IP)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_NETWORK_ROUTER_IP)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_CLIENT_IP)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_ROUTER_IP)->EnableWindow(enable);
    GetDlgItem(IDC_BUTTON_CONNECT_TO_CLIENT)->EnableWindow(enable);
}

void CUstavSRDlg::EnableRouterControlsGroup(BOOL enable)
{
    GetDlgItem(IDC_STATIC_GROUP_ROUTER)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GROUP_GOTO)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GOTO_LATITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_GOTO_LATITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GOTO_LONGITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_GOTO_LONGITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GOTO_ALTITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_GOTO_ALTITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GROUP_LIGHTS)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_LIGHTS_CODE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_LIGHTS)->EnableWindow(enable);

    // Disable buttons
    if (enable == FALSE)
    {
        m_b_sendGoto.EnableWindow(FALSE);
        m_b_sendLights.EnableWindow(FALSE);
    }
}

void CUstavSRDlg::EnableServerControlsGroup(BOOL enable)
{
    GetDlgItem(IDC_STATIC_GROUP_SERVER)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GROUP_DATA)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_YAW)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_YAW)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_PITCH)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_PITCH)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_ROLL)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_ROLL)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_HEADING)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_HEADING)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_LATITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_POS_LATITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_LONGITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_LONGITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_ALTITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_ALTITUDE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_AIRSPEED)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_AIRSPEED)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GROUND_SPEED)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_GROUND_SPEED)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_GROUP_STATUS)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_CONSUMPTION)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_CONSUMPTION)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_BATTERY_LIFE)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_BATTERY_LIFE)->EnableWindow(enable);
    GetDlgItem(IDC_STATIC_UAV_HEALTH)->EnableWindow(enable);
    GetDlgItem(IDC_EDIT_UAV_HEALTH)->EnableWindow(enable);

    // Disable buttons
    if (enable == FALSE)
    {
        m_b_sendData.EnableWindow(FALSE);
        m_b_sendStatus.EnableWindow(FALSE);
    }
}

LONG CUstavSRDlg::OpenSerialPort()
{
    LONG err(0);
    
    // Disable/Enable controls
    EnableSerialControls(FALSE);
    EnableModeControlsGroup(FALSE);
    EnableSendButtons();

    // PORT
    CString portString;
    m_cb_port.GetWindowText(portString);

    // Open port
    if (err = m_serialPort.Open(portString, this))
    {
        LogMessage("Cannot open port: " + portString);
        return err;
    }

    // BAUD
    CString baudString;
    m_cb_baud.GetWindowText(baudString);
    int dwBaud = atoi(baudString);

    // PARITY
    CString parityString;
    m_cb_parity.GetWindowText(parityString);
    
    // DATA BITS
    CString dataBitsString;
    m_cb_dataBits.GetWindowText(dataBitsString);
    int dataBits = atoi(dataBitsString);

    // STOP BITS
    CString stopBitsString;
    m_cb_stopBits.GetWindowText(stopBitsString);

    // Setup the COM port
    if (err = m_serialPort.Setup((CSerial::EBaudrate)dwBaud, (CSerial::EDataBits)dataBits, 
            m_parityMap[parityString], m_stopBitsMap[stopBitsString]))
    {
        LogMessage("Cannot setup port: " + portString);
		return err;
    }

    // FLOW CONTROL (HANDSHAKING)
    CString flowControlString;
    m_cb_flowControl.GetWindowText(flowControlString);
    
    // Setup the COM port
    if (err = m_serialPort.SetupHandshaking(m_flowControlMap[flowControlString]))
    {
        LogMessage("Cannot setup flow control: " + flowControlString);
        return err;
    }

    // Logg    
    LogMessage("Port Opened");

    return err;
}

LONG CUstavSRDlg::CloseSerialPort()
{
	LONG err(0);

    if (err = m_serialPort.Close())
	{
		LogMessage("Cannot close port.");
		return err;
	}

    // Enable/Disable controls
    EnableSerialControls();
    EnableModeControlsGroup();
    EnableSendButtons(FALSE);

    // Logg
    LogMessage("Port Closed");

    return err;
}

void CUstavSRDlg::OnBnClickedButtonOpenClosePort()
{
    if (!UpdateData())
        return;

    // Get open/close button caption
    CString buttonText;
    m_b_openClosePort.GetWindowText(buttonText);

    if (buttonText != "Close Port")
    {
        OpenSerialPort();
        m_b_openClosePort.SetWindowText("Close Port");
    }
    else
    {
        CloseSerialPort();
        m_b_openClosePort.SetWindowText("Open Port");
    }
}

void CUstavSRDlg::OnBnClickedButtonSendData()
{
    if (!UpdateData())
        return;

    // Set US_DATA sentence data
    m_UsData.SetYaw(m_yaw);
    m_UsData.SetPitch(m_pitch);
    m_UsData.SetRoll(m_roll);
    m_UsData.SetHeading(m_heading);
    m_UsData.SetLatitude(m_latitude);
    m_UsData.SetLongitude(m_longitude);
    m_UsData.SetAltitude(m_altitude);
    m_UsData.SetAirspeed(m_airSpeed);
    m_UsData.SetGroundSpeed(m_groundSpeed);

    // Generate sentence
    std::string sentence;
    m_UsData.GenerateSentence(sentence);

    // Send the sentence
    WriteToSerial(sentence.c_str(), sentence.size());
}

void CUstavSRDlg::OnBnClickedButtonSendStatus()
{
    // Check if all fields are valid
    if (!UpdateData())
        return;

    // Set US_STATUS sentence data
    m_UsStatus.SetConsumption(m_consumption);
    m_UsStatus.SetBatteryLife(m_batteryLife);
    m_UsStatus.SetHealth(m_health);

     // Generate sentence
    std::string sentence;
    m_UsStatus.GenerateSentence(sentence);

    // Send the sentence
    m_serialPort.Write(sentence.c_str(), sentence.size());

    // Logg it
    LogMessage(sentence.c_str());
}

void CUstavSRDlg::OnBnClickedRadioModeServer()
{
    EnableNetworkControlsGroup(FALSE);
    EnableRouterControlsGroup(FALSE);
    EnableServerControlsGroup();
    EnableSendButtons(FALSE);
}

void CUstavSRDlg::OnBnClickedRadioModeRouter()
{
    EnableNetworkControlsGroup(FALSE);
    EnableRouterControlsGroup();
    EnableServerControlsGroup(FALSE);
    EnableSendButtons(FALSE);
}

 afx_msg LRESULT CUstavSRDlg::OnSerialMsg (WPARAM wParam, LPARAM lParam)
{
    CSerial::EEvent eEvent = CSerial::EEvent(LOWORD(wParam));
    CSerial::EError eError = CSerial::EError(HIWORD(wParam));

    // lParam: User-defined 32-bit integer (type LPARAM)
    switch (eEvent)
    {
    case CSerialMFC::EEventRecv:
        // TODO: Read data from the port
        ReadFromSerial();
        break;
    }
         
    // Return successful
    return 0;
}

#define BYTES_TO_READ 255
void CUstavSRDlg::ReadFromSerial()
{
    char str[BYTES_TO_READ];
	DWORD bytesRead = 0;

	// Read it
    m_serialPort.Read(&str, BYTES_TO_READ, &bytesRead);
	// Terminate string
	str[bytesRead] = '\0';
    
	// Process recieved string
	if (bytesRead)
	{
		LogMessage(str);
		
		if (m_UrGoto.VerifyDataType(str))
		{
			if (m_UrGoto.ParseSentence(str))
				LogMessage(m_UrGoto.GetError().c_str());
		}
		else if (m_UrLights.VerifyDataType(str))
		{
			if (m_UrLights.ParseSentence(str))
				LogMessage(m_UrLights.GetError().c_str());
		}
		else
		{
			LogMessage("Unrecognized sentence:" + CString(str));
		}
	}
}

void CUstavSRDlg::WriteToSerial(const char* str, size_t size)
{
	DWORD bytesWritten = 0;

    // Write it
    m_serialPort.Write(str, size, &bytesWritten); 

    // Logg it
	if (bytesWritten)
		LogMessage(str);
}